﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenTK;
using DDD.Core;
using DDD.OpenTK;
using FloatComparer = DDDTest.Helper.FloatComparer;

namespace DDDTest.OpenTK.TestImage2D {
    [TestClass]
    public class TestMultiTexture {

        #region Field
        GameWindow window;
        World wld;
        Graphics3D g3d;
        FrameBuffer fbuf;
        #endregion

        #region Setup
        [TestInitialize ()]
        public void MyClassInitialize () {
            window = new GameWindow ();
            wld = CreateWorld ();
            fbuf = CreateFrameBuffer ();

            g3d = Graphics3D.GetInstance ();
            g3d.Reset ();

            g3d.RenderPass += delegate {
                g3d.SetFrameBuffer (fbuf);
                g3d.SetWorld (wld);
                g3d.SetViewort (0, 0, 1, 1);
            };
        }

        [TestCleanup ()]
        public void MyClassCleanup () {
            window.Dispose ();
            window = null;
            wld = null;
        }

        private static FrameBuffer CreateFrameBuffer () {
            var fbuf = new FrameBuffer (8, 0, 0);
            var color0 = new Image2D<byte> (PixelFormat.RGBA8ui, 1, 1);
            var color1 = new Image2D<byte> (PixelFormat.RGBA8ui, 1, 1);
            var color2 = new Image2D<byte> (PixelFormat.RGBA8ui, 1, 1);
            var color3 = new Image2D<byte> (PixelFormat.RGBA8ui, 1, 1);
            var color4 = new Image2D<byte> (PixelFormat.RGBA8ui, 1, 1);
            var color5 = new Image2D<byte> (PixelFormat.RGBA8ui, 1, 1);
            var color6 = new Image2D<byte> (PixelFormat.RGBA8ui, 1, 1);
            var color7 = new Image2D<byte> (PixelFormat.RGBA8ui, 1, 1);
            fbuf.SetColorBuffer (0, color0);
            fbuf.SetColorBuffer (1, color1);
            fbuf.SetColorBuffer (2, color2);
            fbuf.SetColorBuffer (3, color3);
            fbuf.SetColorBuffer (4, color4);
            fbuf.SetColorBuffer (5, color5);
            fbuf.SetColorBuffer (6, color6);
            fbuf.SetColorBuffer (7, color7);
            return fbuf;
        }


        private World CreateWorld () {
            var cam = new Camera ();
            cam.SetPerspective (45, 1, 0.1f, 10f);

            var camNode = new Node ();
            camNode.Attach (cam);
            camNode.SetTranslation (0, 0, 1);

            var pos = new float[3] { 0, 0, 0 };
            var posArray = new VertexArray<float> (1, 3);
            posArray.Set (0, 1, pos);

            var vbuf = new VertexBuffer ();
            vbuf.AddVertexArray ("v_Position", posArray);

            var ibuf = new IndexBuffer<int> (PrimitiveType.Point, 1);
            var indices = new int[1] { 0 };
            ibuf.Set (0, 1, indices);

            var vert = @"#version 400
                         in  vec3 v_Position;
                               
                         void main () {
                             gl_Position = vec4(v_Position, 1);
                         }
                        ";
            var frag = @"#version 400
                         out uvec4 frag_Color0;
                         out uvec4 frag_Color1;
                         out uvec4 frag_Color2;
                         out uvec4 frag_Color3;
                         out uvec4 frag_Color4;
                         out uvec4 frag_Color5;
                         out uvec4 frag_Color6;
                         out uvec4 frag_Color7;

                         uniform usampler2D Texture0;
                         uniform usampler2D Texture1;
                         uniform usampler2D Texture2;
                         uniform usampler2D Texture3;
                         uniform usampler2D Texture4;
                         uniform usampler2D Texture5;
                         uniform usampler2D Texture6;
                         uniform usampler2D Texture7;
                         uniform usampler2D Texture8;
                         uniform usampler2D Texture9;
                         uniform usampler2D Texture10;
                         uniform usampler2D Texture11;
                         uniform usampler2D Texture12;
                         uniform usampler2D Texture13;
                         uniform usampler2D Texture14;
                         uniform usampler2D Texture15;
                         uniform usampler2D Texture16;
                         uniform usampler2D Texture17;
                         uniform usampler2D Texture18;
                         uniform usampler2D Texture19;
                         uniform usampler2D Texture20;
                         uniform usampler2D Texture21;
                         uniform usampler2D Texture22;
                         uniform usampler2D Texture23;
                         uniform usampler2D Texture24;
                         uniform usampler2D Texture25;
                         uniform usampler2D Texture26;
                         uniform usampler2D Texture27;
                         uniform usampler2D Texture28;
                         uniform usampler2D Texture29;
                         uniform usampler2D Texture30;
                         uniform usampler2D Texture31;
                          
                         void main () {
                             frag_Color0 = uvec4 (texture(Texture0, vec2(0.5f,0.5f)).x,
                                                  texture(Texture1, vec2(0.5f,0.5f)).x,
                                                  texture(Texture2, vec2(0.5f,0.5f)).x,
                                                  texture(Texture3, vec2(0.5f,0.5f)).x);
                             frag_Color1 = uvec4 (texture(Texture4, vec2(0.5f,0.5f)).x,
                                                  texture(Texture5, vec2(0.5f,0.5f)).x,
                                                  texture(Texture6, vec2(0.5f,0.5f)).x,
                                                  texture(Texture7, vec2(0.5f,0.5f)).x);
                             frag_Color2 = uvec4 (texture(Texture8, vec2(0.5f,0.5f)).x,
                                                  texture(Texture9, vec2(0.5f,0.5f)).x,
                                                  texture(Texture10, vec2(0.5f,0.5f)).x,
                                                  texture(Texture11, vec2(0.5f,0.5f)).x);
                             frag_Color3 = uvec4 (texture(Texture12, vec2(0.5f,0.5f)).x,
                                                  texture(Texture13, vec2(0.5f,0.5f)).x,
                                                  texture(Texture14, vec2(0.5f,0.5f)).x,
                                                  texture(Texture15, vec2(0.5f,0.5f)).x);
                             frag_Color4 = uvec4 (texture(Texture16, vec2(0.5f,0.5f)).x,
                                                  texture(Texture17, vec2(0.5f,0.5f)).x,
                                                  texture(Texture18, vec2(0.5f,0.5f)).x,
                                                  texture(Texture19, vec2(0.5f,0.5f)).x);
                             frag_Color5 = uvec4 (texture(Texture20, vec2(0.5f,0.5f)).x,
                                                  texture(Texture21, vec2(0.5f,0.5f)).x,
                                                  texture(Texture22, vec2(0.5f,0.5f)).x,
                                                  texture(Texture23, vec2(0.5f,0.5f)).x);
                             frag_Color6 = uvec4 (texture(Texture24, vec2(0.5f,0.5f)).x,
                                                  texture(Texture25, vec2(0.5f,0.5f)).x,
                                                  texture(Texture26, vec2(0.5f,0.5f)).x,
                                                  texture(Texture27, vec2(0.5f,0.5f)).x);
                             frag_Color7 = uvec4 (texture(Texture28, vec2(0.5f,0.5f)).x,
                                                  texture(Texture29, vec2(0.5f,0.5f)).x,
                                                  texture(Texture30, vec2(0.5f,0.5f)).x,
                                                  texture(Texture31, vec2(0.5f,0.5f)).x);
                         }
                        ";

            var vs = new VertexShader (new MemoryStream (Encoding.UTF8.GetBytes (vert)));
            var fs = new FragmentShader (new MemoryStream (Encoding.UTF8.GetBytes (frag)));
            var sp = new ShaderProgram (vs, fs);

            var img = new Image2D<byte>[32];
            for (var i = 0; i < 32; i++) {
                img[i] = new Image2D<byte> (PixelFormat.R8ui, 1, 1);
                img[i].Set (0, 0, 1, 1, new byte[] { (byte)i });
            }

            var sampler = new Sampler[32];
            for (var i = 0; i < 32; i++) {
                sampler[i] = new Sampler (SamplerType.Texture2D);
                sampler[i].SetImage (img[i]);
            }

            var unifs = new UniformArray ();
            for (var i = 0; i < 32; i++) {
                unifs.Add ("Texture" + i, sampler[i]);
            }

            var app = new Appearance ();
            app.SetShaderProgram (sp);
            app.AddUniformArray (unifs);
            for (var i = 0; i < 32; i++) {
                app.AddSampler (sampler[i]);
            }

            var msh = new Mesh ();
            msh.SetVertexBuffer (vbuf);
            msh.SetIndexBuffer (ibuf);
            msh.SetAppearance (app);

            var mshNode = new Node ();
            mshNode.Attach (msh);

            var wld = new World ();
            wld.AddChild (camNode);
            wld.AddChild (mshNode);
            wld.SetActiveCamera (camNode);

            return wld;
        }

        #endregion



        #region TestMethod
        [TestMethod]
        public void Test_MultiTexture () {

            g3d.Render ();
            window.SwapBuffers ();

            var expected0 = new byte[4] { 0, 1, 2, 3 };
            var expected1 = new byte[4] { 4, 5, 6, 7 };
            var expected2 = new byte[4] { 8, 9, 10, 11 };
            var expected3 = new byte[4] { 12, 13, 14, 15 };
            var expected4 = new byte[4] { 16, 17, 18, 19 };
            var expected5 = new byte[4] { 20, 21, 22, 23 };
            var expected6 = new byte[4] { 24, 25, 26, 27 };
            var expected7 = new byte[4] { 28, 29, 30, 31 };
            var colors0 = new byte[4];
            var colors1 = new byte[4];
            var colors2 = new byte[4];
            var colors3 = new byte[4];
            var colors4 = new byte[4];
            var colors5 = new byte[4];
            var colors6 = new byte[4];
            var colors7 = new byte[4];

            var color0 = fbuf.GetColorBuffer (0) as Image2D<byte>;
            var color1 = fbuf.GetColorBuffer (1) as Image2D<byte>;
            var color2 = fbuf.GetColorBuffer (2) as Image2D<byte>;
            var color3 = fbuf.GetColorBuffer (3) as Image2D<byte>;
            var color4 = fbuf.GetColorBuffer (4) as Image2D<byte>;
            var color5 = fbuf.GetColorBuffer (5) as Image2D<byte>;
            var color6 = fbuf.GetColorBuffer (6) as Image2D<byte>;
            var color7 = fbuf.GetColorBuffer (7) as Image2D<byte>;
            color0.Capture ();
            color1.Capture ();
            color2.Capture ();
            color3.Capture ();
            color4.Capture ();
            color5.Capture ();
            color6.Capture ();
            color7.Capture ();
            color0.Get (0, 0, 1, 1, colors0);
            color1.Get (0, 0, 1, 1, colors1);
            color2.Get (0, 0, 1, 1, colors2);
            color3.Get (0, 0, 1, 1, colors3);
            color4.Get (0, 0, 1, 1, colors4);
            color5.Get (0, 0, 1, 1, colors5);
            color6.Get (0, 0, 1, 1, colors6);
            color7.Get (0, 0, 1, 1, colors7);

            CollectionAssert.AreEqual (expected0, colors0);
            CollectionAssert.AreEqual (expected1, colors1);
            CollectionAssert.AreEqual (expected2, colors2);
            CollectionAssert.AreEqual (expected3, colors3);
            CollectionAssert.AreEqual (expected4, colors4);
            CollectionAssert.AreEqual (expected5, colors5);
            CollectionAssert.AreEqual (expected6, colors6);
            CollectionAssert.AreEqual (expected7, colors7);
        }
        #endregion



    }
}
